﻿using System;
using System.Collections.Generic;

using System.Text;
using System.IO;
using System.Text.RegularExpressions;
using Estimate.Common.DBFieldTypes;
using Estimate.Common.Repository;
using Estimate.Common.Entity;
using System.Reflection;
using log4net;
using OperateLogLibrary;

namespace Estimate.Common.DataImporter
{
    /// <summary>
    /// 导入城镇预报数据
    /// </summary>
    public class NormalCityForeImporter : IDataImporter
    {
        protected ForeRepository cityForeRepository;

        public static ILog Logger = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        private OperateLog log;
        public OperateLog Log
        {
            set
            {
                log = value;
            }
            get
            {
                if (log == null)
                    return new OperateLog();
                else
                    return log;
            }
        }

        /// <summary>
        /// 存储 站点;值 数组。其中键值是 span和factor合并成的字符串
        /// </summary>
        protected Dictionary<string, Dictionary<string, string> > processedData;

        /// <summary>
        /// 存储预报站和实况站的对应关系。键值为预报站，值为实况站
        /// </summary>
        protected List<string> stations;

        protected float[] LevList = { 20.0f, 18.0f, 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 13.0f, 14.0f, 21.0f, 26.0f, 8.0f, 15.0f, 22.0f, 27.0f, 9.0f, 16.0f, 23.0f, 28.0f, 10.0f, 17.0f, 24.0f, 11.0f, 25.0f, 12.0f };

        public int GetLevIndex(float lev)
        {
            for (int i = 0; i < LevList.Length - 1; i++)
            {
                if (LevList[i] == lev)
                {
                    return i;
                }
            }

            return 0;
        }

        public float GetMax(float one, float other)
        {
            if( one == other)
                return one;
            else if (GetLevIndex(one) > GetLevIndex(other))
            {
                return one;
            }
            else
            {
                return other;
            }
        }

        public NormalCityForeImporter(ForeRepository repository,string[] sites)
        {
            cityForeRepository = repository;
            processedData = new Dictionary<string, Dictionary<string, string>>();

            stations = new List<string>(sites);            
        }

        /// <summary>
        /// 从文件名获取日期
        /// </summary>
        /// <param name="filename">文件名</param>
        /// <returns>日期</returns>
        private DateTime getDateTimeByFileName(string filename)
        {
            string timestr = filename.Substring(41,12);
            DateTime date = new DateTime(Convert.ToInt32(timestr.Substring(0,4)),Convert.ToInt32(timestr.Substring(4,2)),Convert.ToInt32(timestr.Substring(6,2)));
            date = date.AddHours(8);
            return date;
        }

        /// <summary>
        /// 根据文件名获取起报时间
        /// </summary>
        /// <param name="filename">文件名</param>
        /// <returns>起报时间</returns>
        public ForeClockType GetClockByFileName(string filename) 
        {
            string timestr = filename.Substring(41,12);
            DateTime date = new DateTime(Convert.ToInt32(timestr.Substring(0,4)),Convert.ToInt32(timestr.Substring(4,2)),Convert.ToInt32(timestr.Substring(6,2)));
            date = date.AddHours(Convert.ToInt32(timestr.Substring(8, 2)));
            date =  date.AddHours(8);
            string hourstr = "H"+date.Hour.ToString().PadLeft(2,'0');
            return (ForeClockType)Enum.Parse(typeof(ForeClockType),hourstr);
        }

        /// <summary>
        /// 根据文件名获取发报时间
        /// </summary>
        /// <param name="filename">文件名</param>
        /// <returns>发报时间</returns>
        public ForeSendpointType GetSendpointByFileName(string filename)
        {
            string timestr = filename.Substring(14,14);
            DateTime date = new DateTime(Convert.ToInt32(timestr.Substring(0,4)),Convert.ToInt32(timestr.Substring(4,2)),Convert.ToInt32(timestr.Substring(6,2)));
            date = date.AddHours(Convert.ToInt32(timestr.Substring(8, 2)));
            date = date.AddHours(8);

            if (date.Hour >= 2 && date.Hour < 5)
                return ForeSendpointType.H05;
            else if (date.Hour <= 6)
                return ForeSendpointType.H06;
            else if (date.Hour >= 8 && date.Hour <= 10)
                return ForeSendpointType.H10;
            else if (date.Hour >= 14 && date.Hour < 15)
                return ForeSendpointType.H15;
            else
                return ForeSendpointType.H16;            
        }

        /// <summary>
        /// 检查是否存在指定的站点
        /// </summary>
        /// <param name="st">站点编号</param>
        /// <returns></returns>
        private bool ExistCity(string st)
        {
            return stations.Contains(st);
        }

        public void ClearProcessedData()
        {
            foreach (Dictionary<string,string> value in processedData.Values)
            {
                value.Clear();
            }
        }

        /// <summary>
        /// 判断小时数是否是前12小时
        /// </summary>
        /// <param name="hours">小时数</param>
        /// <returns>是否是前半段</returns>
        private bool IsFirst12(string hours)
        {
            return (Convert.ToInt32(hours) / 12) % 2 == 1;
        }

        /// <summary>
        /// 根据给定的参数，判断出ForeSpanType的值类型
        /// </summary>
        /// <param name="hours">小时数</param>
        /// <param name="is12Hours">是否是12小时制</param>
        /// <returns>ForeSpanType的值</returns>
        private ForeSpanType getForespanFromString(string hours, bool is12Hours)
        {
            switch (hours)
            {
                case "12":
                    return ForeSpanType.FD00_12;
                case "24":
                    if (is12Hours)
                        return ForeSpanType.FD12_24;
                    else
                        return ForeSpanType.FD00_24;
                case "36":
                    return ForeSpanType.FD24_36;
                case "48":
                    if (is12Hours)
                        return ForeSpanType.FD36_48;
                    else
                        return ForeSpanType.FD24_48;
                case "60":
                    return ForeSpanType.FD48_60;
                case "72":
                    if (is12Hours)
                        return ForeSpanType.FD60_72;
                    else
                        return ForeSpanType.FD48_72;
                case "84":
                    return ForeSpanType.FD72_84;
                case "96":
                    if (is12Hours)
                        return ForeSpanType.FD84_96;
                    else
                        return ForeSpanType.FD72_96;
                case "108":
                    return ForeSpanType.FD96_108;
                case "120":
                    if (is12Hours)
                        return ForeSpanType.FD108_120;
                    else
                        return ForeSpanType.FD96_120;
                case "132":
                    return ForeSpanType.FD120_132;
                case "144":
                    if (is12Hours)
                        return ForeSpanType.FD132_144;
                    else
                        return ForeSpanType.FD120_144;
                case "156":
                    return ForeSpanType.FD144_156;
                case "168":
                    if (is12Hours)
                        return ForeSpanType.FD156_168;
                    else
                        return ForeSpanType.FD144_168;
                default:
                    return ForeSpanType.FD00_12;
            }
        }

        /// <summary>
        /// 向预处理的对象中添加一个站点及预测值
        /// </summary>
        /// <param name="span">预测时效</param>
        /// <param name="type">要素</param>
        /// <param name="value">预测站点及值</param>
        private void addData(ForeSpanType span, ForeFactorType type,string stnum, string value)
        {
            string key = string.Format("{0}-{1}", span.ToString(), type.ToString());
            if (!processedData.ContainsKey(key))
                processedData[key] = new Dictionary<string, string>();
            processedData[key][stnum] = value;
        }

        /// <summary>
        /// 将缓存的数据全部存入数据库
        /// </summary>
        /// <param name="date">日期</param>
        /// <param name="clock">起报时间</param>
        /// <param name="sendpoint">发报时间</param>
        public void SaveDataToDataBase(DateTime date, ForeClockType clock, ForeSendpointType sendpoint)
        {
            ForeEntity entity = new ForeEntity();
            entity.Date = date;
            entity.Clock = clock;
            entity.Sendpoint = sendpoint;
            entity.Semitype = ForeSemiType.Auto;

            string pattern = @"-";
            foreach (KeyValuePair<string, Dictionary<string,string>> pair in processedData)
            {
                //去除没有数据的预报
                if (pair.Value.Count == 0)
                    continue;

                string[] keys = Regex.Split(pair.Key, pattern, RegexOptions.IgnoreCase);
                ForeSpanType span = (ForeSpanType)Enum.Parse(typeof(ForeSpanType), keys[0]);
                ForeFactorType type = (ForeFactorType)Enum.Parse(typeof(ForeFactorType), keys[1]);

                entity.Span = span;
                entity.Type = type;
                entity.Value = string.Join("|", pair.Value.Values);
                if (pair.Value.Values.Count < 68 && (pair.Value.Values.Count != 10 || pair.Value.Values.Count != 9) )
                {//11表示，是07212结尾的文件。每天都只有11个站
                    int valideCount;
                    if (sendpoint == ForeSendpointType.H05)
                        valideCount = 10;
                    else
                        valideCount = 68;
                   string info= string.Format("{0} {1} {2} {3} {4}预报数据，少于个{6}站,数目为{5}。semi中将缺失站。",
                        new object[]{
                        date, clock,sendpoint,span,type,
                        pair.Value.Values.Count,
                        valideCount
                    });
                   Logger.Error(info);
                   Log.SetLog(info);
                }
                entity.Semi = "";
                if (DataWillBeSaved(entity))
                {
                    cityForeRepository.SetForeRecord(entity);
                }                
            }            
        }

        /// <summary>
        /// 导入数据文件
        /// </summary>
        /// <param name="filepath">文件路径</param>
        public void ImportDataFromFile(string filepath)
        {
            string filename = Path.GetFileName(filepath);
            DateTime dt = getDateTimeByFileName(filename);
            ImportDataFromFileWithDate(filepath, dt);
        }

        /// <summary>
        /// 城镇报导入数据，有异于其他类型的数据
        /// 城镇报一天会多次生成数据文件，后面的文件是对前面产生文件的修订
        /// 但是存在漏站点的问题
        /// 所以需要修改城镇报入库函数
        /// </summary>
        /// <param name="filepath"></param>
        /// <param name="dt"></param>
        public void ImportDataFromFileWithDate(string filepath, DateTime dt)
        {
            string filename = Path.GetFileName(filepath);

            string pattern = @"\ +";

            StreamReader file = new StreamReader(filepath);

            string line = file.ReadLine();
            line = file.ReadLine();
            line = file.ReadLine();
            line = file.ReadLine();
            line = file.ReadLine();


            string stnum;
            string[] lineItems;
            while (!file.EndOfStream)
            {
                line = file.ReadLine();
                lineItems = Regex.Split(line.TrimStart(), pattern, RegexOptions.IgnoreCase);

                if (lineItems.Length == 6)
                {
                    stnum = lineItems[0];
                    //检查站点，是否是需要入库的数据
                    if (!ExistCity(stnum))
                        continue;
                    //确认当前预报站的记录行数
                    int lines = Convert.ToInt32(lineItems[4]);
                    
                    float wth, preWth,maxt, mint, wd, preWd, ws, preWs;
                    //初始化，不然会编译错误。
                    preWth = 0;
                    preWd = preWs = 0f;

                    ForeSpanType span;
                    for (int i  = 0; i < lines; i++)
                    {
                        line = file.ReadLine();
                        lineItems = Regex.Split(line.TrimStart(), pattern, RegexOptions.IgnoreCase);
                        if (lineItems.Length == 21)
                        {
                            line = line.Replace("1000.0", " 999.9");
                            lineItems = Regex.Split(line.TrimStart(), pattern, RegexOptions.IgnoreCase);
                        }
                        
                        if (lineItems.Length != 22)
                        {
                            if (lineItems.Length == 6)
                            {
                                //已经跳到新的一行了                                
                                stnum = lineItems[0];
                                //检查站点，是否是需要入库的数据
                                if (!ExistCity(stnum))
                                    continue;
                                lines = Convert.ToInt32(lineItems[4]);
                                i = -1;
                            }
                            else
                            {
                                Logger.Error(string.Format("文件：{2}, {0}站的某些行数据的列数不是22,该行数据会被抛弃掉。内容为{1}", stnum, line, filepath));
                                Log.SetLog(string.Format("文件：{2}, {0}站的某些行数据的列数不是22,该行数据会被抛弃掉。内容为{1}", stnum, line, filepath));
                                continue;
                            }
                        }
                        string hours = lineItems[0];
                        if (IsFirst12(hours))
                        {
                            //前12小时，只有三项参数 wth,wd,ws          
                            //这3项参数将在后12小时，进行比较。
                            span = getForespanFromString(hours, true);
                            preWth = wth = Convert.ToSingle(Convert.ToSingle(lineItems[19].ToString()));
                            preWd = wd = Convert.ToSingle(lineItems[20].ToString());
                            preWs = ws = Convert.ToSingle(lineItems[21].ToString());

                            addData(span, ForeFactorType.wth, stnum, string.Format("{0};{1}", stnum, wth.ToString()));
                            addData(span, ForeFactorType.wd, stnum, string.Format("{0};{1}", stnum, wd.ToString()));
                            addData(span, ForeFactorType.ws, stnum, string.Format("{0};{1}", stnum, ws.ToString()));

                        }
                        else
                        {
                            span = getForespanFromString(hours, true);
                            wth = Convert.ToSingle(Convert.ToSingle(lineItems[19].ToString()));
                            wd = Convert.ToSingle(lineItems[20].ToString());
                            ws = Convert.ToSingle(lineItems[21].ToString());

                            addData(span, ForeFactorType.wth, stnum, string.Format("{0};{1}", stnum, wth.ToString()));
                            addData(span, ForeFactorType.wd, stnum, string.Format("{0};{1}", stnum, wd.ToString()));
                            addData(span, ForeFactorType.ws, stnum, string.Format("{0};{1}", stnum, ws.ToString()));

                            //将3项参数进行24小时的比对。取最大的算24小时制的结果
                            span = getForespanFromString(hours, false);
                            wth = GetMax(wth, preWth);
                            wd = Math.Max(wd, preWd);
                            ws = Math.Max(ws, preWs);
                            mint = Convert.ToSingle(lineItems[12].ToString());
                            maxt = Convert.ToSingle(lineItems[11].ToString());

                            addData(span, ForeFactorType.wth, stnum, string.Format("{0};{1}", stnum, wth.ToString()));
                            addData(span, ForeFactorType.wd, stnum, string.Format("{0};{1}", stnum, wd.ToString()));
                            addData(span, ForeFactorType.ws, stnum, string.Format("{0};{1}", stnum, ws.ToString()));
                            addData(span, ForeFactorType.mint, stnum, string.Format("{0};{1}", stnum, mint.ToString()));
                            addData(span, ForeFactorType.maxt, stnum, string.Format("{0};{1}", stnum, maxt.ToString()));
                        }
                    }
                }

            }      
        }

        /// <summary>
        /// 在保存数据前引发 BeforeDataInDatabase 信号
        /// 并将参数中是否入库的变量返回
        /// </summary>
        /// <param name="score">即将入库的数据</param>
        /// <returns>是否入库。 true则表示将入库。 false则表示不会入库</returns>
        public bool DataWillBeSaved(ForeEntity entity)
        {
            object data = entity.Clone();
            if (BeforeDataInDatabase != null)
            {
                FindDataEventArgs args = new FindDataEventArgs(data);
                BeforeDataInDatabase(this, args);
                return args.SaveData;
            }
            return true;
        }


        /// <summary>
        /// 未实现该事件。目前用不到
        /// </summary>
        public event EventHandler AfterDataInDatabase;


        
        public event EventHandler BeforeDataInDatabase;

    }
}
